#include "WhyEventSystem.h"

#include <Windows.h>
#include <process.h>

extern CRITICAL_SECTION criticalSection;

unsigned __stdcall EventWorkerThread(void* param)
{
	WhyEventSystem* eventSystem = (WhyEventSystem*)param;

	while(eventSystem->IsProcessing())
	{
		eventSystem->ProcessEvents();
	}

	return 0;
}

void WhyEventSystem::Init()
{
	processing = true;
	eventHandle = (HANDLE)_beginthreadex(NULL, 0, EventWorkerThread, this, 0, 0); 
}

void WhyEventSystem::Shutdown()
{
	processing = false;

	WaitForSingleObject(eventHandle, INFINITE);

	CloseHandle(eventHandle);
}

bool WhyEventSystem::AlreadyRegistered(const char* eventName, IEventListener* listener)
{
	pair<multimap<const char*, IEventListener*>::iterator,
		 multimap<const char*, IEventListener*>::iterator> range;

	range = clients.equal_range(eventName);

	for(multimap<const char*, IEventListener*>::iterator mmIter = range.first; mmIter != range.second; mmIter++)
	{
		if((*mmIter).second == listener)
		{
			return true;
		}
	}

	return false;
}

void WhyEventSystem::RegisterClient(const char* eventName, IEventListener* listener)
{
	if (!listener || AlreadyRegistered(eventName, listener))
		return;

	EnterCriticalSection(&criticalSection);

	clients.insert(make_pair(eventName, listener));

	LeaveCriticalSection(&criticalSection);
}

void WhyEventSystem::UnregisterClient(const char* eventName, IEventListener* listener)
{
	pair<multimap<const char*, IEventListener*>::iterator,
		 multimap<const char*, IEventListener*>::iterator> range;

	range = clients.equal_range(eventName);

	for(multimap<const char*, IEventListener*>::iterator mmIter = range.first; mmIter != range.second; mmIter++)
	{
		if((*mmIter).second == listener)
		{
			EnterCriticalSection(&criticalSection);

			mmIter = clients.erase(mmIter);

			LeaveCriticalSection(&criticalSection);

			break;
		}
	}
}

void WhyEventSystem::UnregisterClientAll()
{
	clients.clear();
}

void WhyEventSystem::SendEvent(IEventData* eventdata)
{
	events.push(eventdata);
}

void WhyEventSystem::ProcessEvents()
{
	queue<IEventData*> copyEvents;

	EnterCriticalSection(&criticalSection);

	while(!events.empty())
	{
		copyEvents.push(events.front());
		events.pop();
	}

	LeaveCriticalSection(&criticalSection);

	pair<multimap<const char*, IEventListener*>::iterator,
		 multimap<const char*, IEventListener*>::iterator> range;

	while(!copyEvents.empty())
	{
		IEventData* eventdata = events.front();
		copyEvents.pop();

		range = clients.equal_range(eventdata->GetEventID());

		for(multimap<const char*, IEventListener*>::iterator mmIter = range.first; mmIter != range.second; mmIter++)
		{
			(*mmIter).second->HandleEvent(eventdata);
		}
	}
}